﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Aga.Controls.Tree;
using System.IO;
using System.Reflection;
using UnrealDebugger.Panels;

namespace UnrealDebugger.Controls
{
    public partial class OutlineControl : UserControl
    {
        public class BookmarkNode : Node
        {
            public BookmarkNode()
                : base()
            {
            }

            public BookmarkNode(string text, Categories category, ScriptOutline.ScriptDesc description)
                : base(text)
            {
                this._category = category;
                this._description = description;
            }

            public enum Categories
            {
                None,
                Class,
                Group,
                Variable,
                Function,
                Event,
                State,
                DefaultProperties,
                Replication,
                CategoryCount,
            }

            public Image Icon { get { return _categories[(int)_category]; } }

            public string Details { get { return _description != null ? _description.Statement : ""; } }

            public ScriptOutline.ScriptDesc Description { get { return _description; } }

            protected Categories _category = Categories.None;
            protected ScriptOutline.ScriptDesc _description = null;

            protected static Image[] _categories = new Image[(int)Categories.CategoryCount];

            static BookmarkNode()
            {
                Stream file = null;
                Assembly assembly = typeof(BookmarkNode).Assembly;

                for (int idx = (int)Categories.Class; idx < (int)Categories.CategoryCount; ++idx)
                {
                    file = assembly.GetManifestResourceStream(string.Format("UnrealDebugger.Resources.{0}.png", ((Categories)idx).ToString()));
                    _categories[idx] = Image.FromStream(file);
                }
            }
        }

        private TreeModel _model = null;
        private ScriptOutline _outline = null;
        public ScriptOutline Outline { get { return _outline; } }

        public OutlineControl()
        {
            InitializeComponent();
            this.Dock = DockStyle.Fill;
        }

        internal void GotoLine(bool focusDocument)
        {
            foreach (TreeNodeAdv node in this.treeViewOutline.SelectedNodes)
            {
                BookmarkNode bookmarkNode = node.Tag as BookmarkNode;
                int line = bookmarkNode != null && bookmarkNode.Description != null ? bookmarkNode.Description.Line : -1;

                if (line >= 0)
                {
                    Document document = UnrealDebuggerIDE.Instance.GetDocumentWithFocus();
                    document.GotoLine(line, 0);

                    if (focusDocument)
                    {
                        document.Show();
                    }
                }
            }
        }

        internal void ParseScript(string file)
        {
            this.treeViewOutline.BeginUpdate();

            this._model = new TreeModel();
            this._outline = ScriptOutline.CreateFromScript(file);

            if (this._outline != null)
            {
                BookmarkNode rootNode = new BookmarkNode(this._outline.Class, BookmarkNode.Categories.Class, null);

                this._model.Nodes.Add(rootNode);

                try
                {
                    BookmarkNode replicationNode = new BookmarkNode("Replication", BookmarkNode.Categories.Replication, this._outline.Replication);
                    rootNode.Nodes.Add(replicationNode);

                    BookmarkNode defaultPropertiesNode = new BookmarkNode("Default Properties", BookmarkNode.Categories.DefaultProperties, this._outline.DefaultProperties);
                    rootNode.Nodes.Add(defaultPropertiesNode);

                    BookmarkNode varsNode = new BookmarkNode("Variables", BookmarkNode.Categories.Group, null);
                    rootNode.Nodes.Add(varsNode);

                    foreach (ScriptOutline.VarDesc scriptVar in this._outline.Variables)
                    {
                        BookmarkNode varNode = new BookmarkNode(scriptVar.Identifier, BookmarkNode.Categories.Variable, scriptVar);
                        varsNode.Nodes.Add(varNode);
                    }

                    BookmarkNode stateNode = null;
                    Dictionary<string, BookmarkNode> states = new Dictionary<string, BookmarkNode>();
                    BookmarkNode functionsNode = new BookmarkNode("Functions", BookmarkNode.Categories.Group, null);
                    rootNode.Nodes.Add(functionsNode);

                    states.Clear();
                    stateNode = new BookmarkNode("\tGlobal", BookmarkNode.Categories.State, null);
                    states["\tGlobal"] = stateNode;
                    functionsNode.Nodes.Add(stateNode);

                    foreach (ScriptOutline.StateDesc scriptState in this._outline.States)
                    {
                        stateNode = new BookmarkNode(scriptState.Identifier, BookmarkNode.Categories.State, scriptState);
                        states[scriptState.Identifier] = stateNode;
                        functionsNode.Nodes.Add(stateNode);
                    }

                    foreach (ScriptOutline.FunctionDesc scriptFunction in this._outline.Functions)
                    {
                        BookmarkNode functionNode = new BookmarkNode(scriptFunction.Identifier, BookmarkNode.Categories.Function, scriptFunction);

                        if (states.TryGetValue(scriptFunction.State, out stateNode))
                        {
                            stateNode.Nodes.Add(functionNode);
                        }
                    }

                    BookmarkNode eventsNode = new BookmarkNode("Events", BookmarkNode.Categories.Group, null);
                    rootNode.Nodes.Add(eventsNode);

                    states.Clear();
                    stateNode = new BookmarkNode("\tGlobal", BookmarkNode.Categories.State, null);
                    states["\tGlobal"] = stateNode;
                    eventsNode.Nodes.Add(stateNode);

                    foreach (ScriptOutline.StateDesc scriptState in this._outline.States)
                    {
                        stateNode = new BookmarkNode(scriptState.Identifier, BookmarkNode.Categories.State, scriptState);
                        states[scriptState.Identifier] = stateNode;
                        eventsNode.Nodes.Add(stateNode);
                    }

                    foreach (ScriptOutline.EventDesc scriptEvent in this._outline.Events)
                    {
                        BookmarkNode eventNode = new BookmarkNode(scriptEvent.Identifier, BookmarkNode.Categories.Event, scriptEvent);

                        if (states.TryGetValue(scriptEvent.State, out stateNode))
                        {
                            stateNode.Nodes.Add(eventNode);
                        }
                    }
                }
                catch (Exception e)
                {
                    ExceptionMessageBox msgBox = new ExceptionMessageBox(e);
                    msgBox.ShowDialog();
                }
            }

            this.treeViewOutline.Model = _model;
            this.treeViewOutline.EndUpdate();

            foreach (TreeNodeAdv node in this.treeViewOutline.Root.Children)
            {
                node.Expand();
            }
        }

        private void treeViewOutline_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Return)
            {
                GotoLine(true);
            }
        }

        private void treeViewOutline_DoubleClick(object sender, EventArgs e)
        {
            GotoLine(true);
        }

        private void treeViewOutline_SelectionChanged(object sender, EventArgs e)
        {
            GotoLine(false);
            this.treeViewOutline.Focus();
        }
    }
}
